/*  
 * Actipro Software's WPF Docking & MDI / Prism Integration Add-on
 * http://actipro.codeplex.com/
 * 
 * Copyright (c) 2009-2015 Actipro Software LLC. 
 *  
 * This source code is subject to the terms of the Microsoft Public License (Ms-PL). 
 *  
 * Redistribution and use in source and binary forms, with or without modification, 
 * is permitted provided that redistributions of the source code retain the above 
 * copyright notices and this file header. 
 *  
 * Additional copyright notices should be appended to the list above. 
 * 
 * For details, see <http://www.opensource.org/licenses/ms-pl.html>. 
 *  
 * All other rights reserved. 
 *
 */  

using System.Collections.Specialized;
using System.Windows;
using ActiproSoftware.Windows.Controls.Docking;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.Regions.Behaviors;

namespace ActiproSoftware.Windows.Prism.Regions.Behaviors {

	/// <summary>
	/// Defines the attached behavior that keeps the items of the <see cref="DockSite"/> host control in synchronization with the <see cref="IRegion"/>.
	/// </summary>
	/// <remarks>
	/// This behavior also makes sure that if a view is in the region is activated, then it's associated window in the <see cref="DockSite"/> is activated.
	/// In addition, if you active a window in the <see cref="DockSite"/>, then it's associated view is activated in the region.
	/// </remarks>
	public class DockSiteRegionBehavior : RegionBehavior, IHostAwareRegionBehavior {

		private DockSite hostControl;

		/// <summary>
		/// Name that identifies the <see cref="DockSiteRegionBehavior"/> behavior in a collection of regions behaviors.
		/// </summary>
		public static readonly string BehaviorKey = "DockSiteRegionBehavior";

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		#region INTERFACE IMPLEMENTATION
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		#region IHostAwareRegionBehavior Members

		/// <summary>
		/// Gets or sets the <see cref="DockSite"/> that the <see cref="IRegion"/> is attached to.
		/// </summary>
		/// <value>A <see cref="DockSite"/> that the <see cref="IRegion"/> is attached to.</value>
		public DependencyObject HostControl {
			get { return this.hostControl; }
			set { this.hostControl = value as DockSite; }
		}

		#endregion // IHostAwareRegionBehavior Members

		#endregion // INTERFACE IMPLEMENTATION

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		#region NON-PUBLIC PROCEDURES
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Handles the <c>WindowActivated</c> event of the <c>DockSite</c> control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="DockingWindowEventArgs"/> instance containing the event data.</param>
		private void OnDockSiteWindowActivated(object sender, DockingWindowEventArgs e) {
			// 3/1/2012 - Added check to verify that the DockingWindow is registered with the DockSite associated with this instance (245-16E10F4B-0305)
			DockingWindow dockingWindow = e.Window;
			if (dockingWindow == null || dockingWindow.DockSite != this.hostControl)
				return;

			IRegion region = this.Region;
			if (region == null)
				return;

			foreach (object activeView in region.ActiveViews) {
				if (activeView != dockingWindow && activeView != dockingWindow.DataContext)
					region.Deactivate(activeView);
			}

			if (region.Views.Contains(dockingWindow))
				region.Activate(dockingWindow);
			// 8/3/2011 - Changed Activate to use DataContext instead of Content to allow retention of visuals
			else if (region.Views.Contains(dockingWindow.DataContext))
				region.Activate(dockingWindow.DataContext);
		}

		/// <summary>
		/// Handles the <c>WindowDeactivated</c> event of the <c>DockSite</c> control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="DockingWindowEventArgs"/> instance containing the event data.</param>
		private void OnDockSiteWindowDeactivated(object sender, DockingWindowEventArgs e) {
			// 3/1/2012 - Added check to verify that the DockingWindow is registered with the DockSite associated with this instance (245-16E10F4B-0305)
			DockingWindow dockingWindow = e.Window;
			if (dockingWindow == null || dockingWindow.DockSite != this.hostControl)
				return;

			IRegion region = this.Region;
			if (region == null)
				return;

			if (region.ActiveViews.Contains(dockingWindow))
				region.Deactivate(dockingWindow);
			else if (region.ActiveViews.Contains(dockingWindow.DataContext))
				region.Deactivate(dockingWindow.DataContext);
		}

		/// <summary>
		/// Handles the <c>WindowRegistered</c> event of the <c>DockSite</c> control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="DockingWindowEventArgs"/> instance containing the event data.</param>
		private void OnDockSiteWindowRegistered(object sender, DockingWindowEventArgs e) {
			// Ensure the DockingWindow exists and is generated for an item
			// 3/1/2012 - Added check to verify that the DockingWindow is registered with the DockSite associated with this instance (245-16E10F4B-0305)
			DockingWindow dockingWindow = e.Window;
			if (this.hostControl == null || dockingWindow == null || !dockingWindow.IsContainerForItem || dockingWindow.DockSite != this.hostControl)
				return;

			// Initialize variables
			object item = dockingWindow.DataContext ?? dockingWindow;

			// Check for initializer and if found then initialize
			IDockingWindowInitializer provider = DockSiteRegionAdapter.GetDockingWindowInitializer(item);
			if (provider != null) {
				provider.Initialize(dockingWindow, item);
				provider.Open(dockingWindow, item);
			}
		}

		/// <summary>
		/// Handles the <c>CollectionChanged</c> event of the <c>Region.ActiveViews</c> collection.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
		private void OnRegionActiveViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
			IRegion region = this.Region;
			DockSite dockSite = this.hostControl;
			if (region == null || dockSite == null || e.NewItems == null)
				return;

			foreach (object item in e.NewItems) {
				var dockingWindow = dockSite.ContainerFromItem(item);
				if (dockingWindow == null)
					continue;
				else if (!dockingWindow.IsOpen) {
					// 1/28/2011 - Added code to check for IDockingWindowInitializer to perform open
					IDockingWindowInitializer provider = DockSiteRegionAdapter.GetDockingWindowInitializer(item);
					if (provider != null)
						provider.Open(dockingWindow, item);
					else
						dockingWindow.Open();
				}
				dockingWindow.Activate(e.NewItems.Count == 1);
			}
		}

		#endregion // NON-PUBLIC PROCEDURES

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		#region PUBLIC PROCEDURES
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Override this method to perform the logic after the behavior has been attached.
		/// </summary>
		protected override void OnAttach() {
			IRegion region = this.Region;
			if (region != null)
				region.ActiveViews.CollectionChanged += this.OnRegionActiveViewsCollectionChanged;

			DockSite dockSite = this.hostControl;
			if (dockSite != null) {
				dockSite.WindowActivated += this.OnDockSiteWindowActivated;
				dockSite.WindowDeactivated += this.OnDockSiteWindowDeactivated;
				dockSite.WindowRegistered += this.OnDockSiteWindowRegistered;
			}
		}

		#endregion // PUBLIC PROCEDURES
	}
}
